import json
import exifread

from urllib.request import urlopen, Request
from geopy.point import Point
from geopy.geocoders import Nominatim, ArcGIS


def image_exif(folder: str, file: str) -> tuple:
    """获取照片文件的GPS信息，返回经、纬度和高度值

    Args:
        folder (str): 照片文件所在的文件夹
        file (str): 照片文件的文件名

    Returns:
        tuple: lng/经度:float, lat/纬度:float, alt/高度:float
    """
    photo_file = folder + "/" + file
    with open(photo_file, 'rb') as img_file:
        # 读取照片元数据，image_map是字典数据结构
        image_map = exifread.process_file(img_file)

    # str:'11793/100' -> 117.93
    altitude = eval(image_map['GPS GPSAltitude'].printable)
    # altitude = image_map['GPS GPSAltitude'].values

    # 经度：'[114, 24, 17308959/1000000]' -> [114, 24, 17.308959]
    img_lng = eval(image_map["GPS GPSLongitude"].printable)
    # img_lng = image_map["GPS GPSLongitude"].values

    # DMS -> Decimal Degree
    longitude = Point.parse_degrees(img_lng[0], img_lng[1], img_lng[2])

    # 纬度：'[30, 29, 85437/20000]' -> [30, 29, 4.27185]
    img_lat = eval(image_map['GPS GPSLatitude'].printable)
    # img_lat = image_map['GPS GPSLatitude'].values

    # DMS -> Decimal Degree
    latitude = Point.parse_degrees(img_lat[0], img_lat[1], img_lat[2])

    return longitude, latitude, altitude


def reverse_nominatim(longitude: float, latitude: float) -> str:
    """根据经纬度值，查询对应的地址

    Args:
        longitude (float): 经度值
        latitude (float): 纬度值

    Returns:
        str: 具体的地址信息
    """

    # 不能访问，尝试：geo_locator = ArcGIS()
    geo_locator = Nominatim(user_agent='Photo_Place')
    # 按地点(latitude, longitude)返回地址
    # 注意参数顺序，纬度在前，经度在后
    query_point = (latitude, longitude)
    return geo_locator.reverse(query_point).address


def tianditu_geocode(appkey: str, point: tuple, domain='http://api.tianditu.gov.cn', ver=1) -> dict:
    """使用天地图逆地理服务API将坐标点（经纬度）转换为结构化的地址信息

       使用逆地理编码服务需要申请Key

    Args:
        point(tuple): (lon/坐标的x值, lat/坐标的y值)
        appkey (str): 开发者申请的Key
        domain (str, optional): API URL的域名
        ver (int, optional): 接口版本，默认为版本1

    Returns:
        dict: 逆地理编码查询结果
    """

    # 查询要提交的数据
    post_dict = {'lon': point[0], 'lat': point[1], 'ver': ver}
    # dict -> str
    post_str = json.dumps(post_dict).replace(' ', '')
    api_url = f"{domain}/geocoder?postStr={post_str}&type=geocode&tk={appkey}"

    # 返回结果
    # {'formatted_address': None, 'addressComponent': None,
    #   point:{'longitude': None, 'latitude': None}}
    location = dict.fromkeys(
        ('formatted_address', 'addressComponent', 'point'))

    # 发起查询request请求
    request = Request(api_url)
    with urlopen(request) as response:
        response_text = response.read().decode('utf-8')
        response_json = json.loads(response_text)

    location['formatted_address'] = response_json['result']['formatted_address']
    location['point'] = response_json['result']['location']
    location['addressComponent'] = response_json['result']['addressComponent']

    return location


def tianditu_geocodeV2(appkey: str, *point, **postStr) -> dict:
    """使用天地图逆地理服务API将坐标点（经纬度）转换为结构化的地址信息

    改造tianditu_geocode()函数，以支持不定长参数调用

    Args:
        appkey (str): 开发者申请的Key

    Returns:
        dict: 逆地理编码查询结果
    """

    # tianditu_geocode()函数中，形参point本身就是tuple类型
    # 该行代码可以不要，可以直接传递形参point
    point = tuple(point)

    domain = postStr['domain']
    ver = postStr['ver']
    # 调用tianditu_geocode实现逆地理查询
    location = tianditu_geocode(appkey, point, domain, ver)

    return location


if __name__=="__main__":
    directory = 'photo_place/data'
    file_name = 'IMG_20191221_101137.jpg'

    # 任务1：获取照片的经纬度信息
    # 位置参数/返回多个值
    # lng, lat, alt = image_exif(directory, file_name)
    # print('照片的GPS信息：({0},{1},{2})'.format(lng, lat, alt))


    # 任务2：根据经纬度查询地址
    # 命名参数
    # (114.40480804416667,30.484519958333333)
    # address = reverse_nominatim(lng, lat)
    # address = reverse_nominatim(114.40480804416667, 30.484519958333333)
    # 错误
    # address = reverse_nominatim(30.484519958333333,114.40480804416667)
    # address = reverse_nominatim(
    #     latitude=30.484519958333333, longitude=114.40480804416667)
    # print("照片的地址是：{}".format(address))


    # 任务3：通过API地理逆编码解析地址
    # 默认值参数
    # point = (lng, lat)
    # 你自己的密钥
    # api_key = ''
    # location = tianditu_geocode(api_key, point)
    # print(location)


    # 任务4：通过API地理逆编码解析地址v2
    # 不定长参数
    # point = (114.40480804416667, 30.484519958333333)
    # post_data = {'domain': 'http://api.tianditu.gov.cn', 'ver': 1}
    # 你自己的密钥
    # api_key = ' '
    # 1）直接传值调用
    # location = tianditu_geocodeV2(api_key, 114.40480804416667, 30.484519958333333,
    #                             domain='http://api.tianditu.gov.cn', ver=1)
    # 2）变量解包传值调用
    # location = tianditu_geocodeV2(api_key, *point, **post_data)

    # print(location)
